﻿using System.Collections.Generic;
using System.IO;
using System.Linq;

using DC2010.Logic;
using DC2010.Objects.Base;
using log4net;

namespace DC2010.Systems
{
    public abstract class BaseSystem
    {
        public abstract void Init();
    }

    public abstract class BaseSystem<TInfo, TGfx, TData> 
        where TInfo : BaseObject
        where TGfx: BaseObject
        where TData: BaseObject
    {
        /// <summary>
        /// Log utility
        /// </summary>
        protected readonly ILog log = LogManager.GetLogger(typeof(BaseSystem<TInfo, TGfx, TData>));

        /// <summary>
        /// In this file is stored, all definition data. Those are the data which are
        /// NOT CHANGED during gameplay. For example creature name, item properties, etc...
        /// </summary>
        public string InfoFileName { get; set; }

        /// <summary>
        /// This file stores all the gfx data. Which means - name of texture,
        /// coordinates on the screen etc. Generally, all the data I need for object display in game.
        /// </summary>
        public string GfxFileName { get; set; }

        /// <summary>
        /// This file contains data which chages during gameplay. For example torch is burning down
        /// so with time this torch become unusable. I have to store it somewhere. This is the place.
        /// Item charges are defined in info, but im taking value from data. So this means that when im
        /// creating new object i have to handle data filling routine.
        /// </summary>
        public string DataFileName { get; set; }

        /// <summary>
        /// Creature, Item ....core of filename. Used for save games.
        /// </summary>
        public string Code { get; set; }

        /// <summary>
        /// Dictionary, keyed by ID
        /// </summary>
        public Dictionary<string, TInfo> DictInfo { get; set; }

        /// <summary>
        /// Dictionary, keyed by ID
        /// </summary>
        public Dictionary<string, TGfx> DictGfx { get; set; }

        /// <summary>
        /// Dictionary, keyed by ID
        /// </summary>
        public Dictionary<string, TData> DictData { get; set; }
		
		// Not all systems have to use all files.
		public bool IsUsingInfo {get;set;}
		public bool IsUsingGfx {get;set;}
		public bool IsUsingData {get;set;}

        public virtual void Init(string code)
        {
            DictInfo = new Dictionary<string, TInfo>();
            DictGfx = new Dictionary<string, TGfx>();
            DictData = new Dictionary<string, TData>();

            LoadInfo();
            LoadGfx();
        }

        /// <summary>
        /// Store Info
        /// </summary>
        public void SaveInfo()
        {
            List<TInfo> ltinfo = (from x in DictInfo
                                  select x.Value).ToList();

            Serializer<List<TInfo>>.Serialize(InfoFileName, ltinfo);
            //LoadInfo();
        }

        /// <summary>
        /// Store Gfx
        /// </summary>
        public void SaveGfx()
        {
            List<TGfx> ltgfx = (from x in DictGfx
                                  select x.Value).ToList();

            Serializer<List<TGfx>>.Serialize(GfxFileName, ltgfx);
        }

        /// <summary>
        /// Store data
        /// </summary>
        public void SaveData()
        {
            List<TData> ltdata = (from x in DictData
                                  select x.Value).ToList();

            Serializer<List<TData>>.Serialize(DataFileName, ltdata);
            //LoadData();
        }

        /// <summary>
        /// Load Info
        /// </summary>
        public void LoadInfo()
        {
			if (IsUsingInfo)
			{
				DictInfo.Clear();
				List<TInfo> lt = Serializer<List<TInfo>>.Deserialize(InfoFileName);

				if (lt != null)
				{
					foreach (TInfo be in lt)
					{
						if (!DictInfo.ContainsKey(be.Id))
							DictInfo.Add(be.Id, be);
					}
				}
			}
        }

        /// <summary>
        /// Load Gfx
        /// </summary>
        public void LoadGfx()
        {
			if (IsUsingGfx)
			{		
				List<TGfx> lt = Serializer<List<TGfx>>.Deserialize(GfxFileName);

				if (lt != null)
				{
					foreach (TGfx be in lt)
					{
						if (!DictGfx.ContainsKey(be.Id))
							DictGfx.Add(be.Id, be);
					}
				}
			}
        }

        /// <summary>
        /// Load Data
        /// </summary>
        public void LoadData()
        {
			if (IsUsingData)
			{		
				DictData.Clear();
				//DataFileName = Path.Combine(Root.I.GameDirectory, "Data\\" + Code + ".xml");

				List<TData> lt = Serializer<List<TData>>.Deserialize(DataFileName);

				if (lt != null)
				{
					foreach (TData be in lt)
					{
						if (!DictData.ContainsKey(be.Id))
							DictData.Add(be.Id, be);
					}
				}
			}
        }

        /// <summary>
        /// Add info 
        /// </summary>
        /// <param name="obj">Info object</param>
        /// <param name="id">ID</param>
        public void AddInfo(TInfo obj)
        {
            if (!DictInfo.ContainsKey(obj.Id))
            {
                DictInfo.Add(obj.Id, obj);
            }

            SaveInfo();
        }

        /// <summary>
        /// Remove Info
        /// </summary>
        /// <param name="obj">Id</param>
        public void RemoveInfo(TInfo obj)
        {
            if (DictInfo.ContainsKey(obj.Id))
            {
                DictInfo.Remove(obj.Id);
                SaveInfo();
            }
        }

        /// <summary>
        /// Add gfx
        /// </summary>
        /// <param name="obj">Gfx object</param>
        /// <param name="id">Id</param>
        public void AddGfx(TGfx obj)
        {
            if (!DictGfx.ContainsKey(obj.Id))
            {
                DictGfx.Add(obj.Id, obj);
            }

            SaveGfx();
        }

        /// <summary>
        /// Remove Gfx
        /// </summary>
        /// <param name="obj">Object to remove</param>
        public void RemoveGfx(TGfx obj)
        {
            if (DictGfx.ContainsKey(obj.Id))
            {
                DictGfx.Remove(obj.Id);
                SaveGfx();
            }
        }

        /// <summary>
        /// Add data
        /// </summary>
        /// <param name="obj">Data to add</param>
        /// <param name="id">Id</param>
        public void AddData(TData obj)
        {
            if (!DictData.ContainsKey(obj.Id))
            {
                DictData.Add(obj.Id, obj);
            }

            SaveData();
        }

        /// <summary>
        /// Remove data
        /// </summary>
        /// <param name="obj">Object to remove</param>
        public void RemoveData(TData obj)
        {
            if (DictData.ContainsKey(obj.Id))
            {
                DictData.Remove(obj.Id);
                SaveData();
            }
        }

        public TInfo GetInfo(string code)
        {
            if (string.IsNullOrEmpty(code))
            {
                log.ErrorFormat("Cannot get info about object: {0}", code);
                return null;
            }

            if (DictInfo.ContainsKey(code))
                return DictInfo[code];

            //log.ErrorFormat("Cannot get info about object: {0}", code);
            return null;
        }

        public TData GetData(string code)
        {
            if (string.IsNullOrEmpty(code))
            {
                log.ErrorFormat("Cannot get data for object: {0}", code);
                return null;
            }

            if (DictData.ContainsKey(code))
                return DictData[code];

            //log.ErrorFormat("Cannot get data for object: {0}", code);
            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="code">Code is same for GFX and INFO. In other words.
        /// I have to know how to draw what i sooner defined</param>
        /// <returns></returns>
        public TGfx GetGfx(string code)
        {
            if (string.IsNullOrEmpty(code))
            {
                log.ErrorFormat("Cannot get gfx for object: {0}", code);
                return null;
            }

            if (DictGfx.ContainsKey(code))
                return DictGfx[code];

            log.ErrorFormat("Cannot get gfx for object: {0}", code);
            return null;
        }
    }
}
